Composables 是 Vue 3 Composition API 的重要概念,將共用的有狀態邏輯(stateful logic)寫成一個可重用的函式以提高可維護性與測試性,在 Nuxt 3 使用時並無須手動引入(Auto-imports 👍)。
Nuxt 3 預設有多組內建的 Composables,詳細內容可參考官方文件,以下舉幾個常見的例子。
提供了一種 Nuxt 執行時存取 context 的方法,在伺服器端與客戶端都可以使用,且幫助我們存取 Vue 實體、Hooks、設定值與其他內部狀態等。
// app.vue
const nuxtApp = useNuxtApp()
console.log(nuxtApp)
provide (name, value)
// app.vue
const nuxtApp = useNuxtApp()
nuxtApp.provide('hello', (name) => `Hello ${name}!`)
console.log(nuxtApp.$hello('Kitty'))
Server-Side
Client-Side
提供如同 document.cookie 的功能,可以讓 Web 伺服器記住使用者的狀態資訊(ex:登入者的 token、購物車或喜愛商品)。
// app.vue
<template>
<div>
<h1>Counter: {{ counter }}</h1>
<button @click="counter = null">reset</button>
<button @click="counter--">-</button>
<button @click="counter++">+</button>
</div>
</template>
// app.vue
<script setup lang="ts">
const counter = useCookie('counter')
counter.value = 0
</script>
瀏覽器畫面顯示。
已建立名稱為 counter
的 cookie,且值為 0。
useCookie('counter')
表使用名稱為 counter
的 cookie。 但須注意此處不代表就建立 counter
的 cookie,而是需要 counter.value = 0
,賦值之後才會建立,如果少了這段,counter--
或 counter++
會出現錯誤 NAN
。
如果希望在 useCookie
直接加上預設值也是可以的,只需在後面加上 default
值。
const counter = useCookie('counter', {
default: () => 0,
})
清除 cookie 只需 counter = null
,cookie 就會被刪除。
⚠ 注意:官方文件有說明 "useCookie only works during setup or Lifecycle Hooks.",如果在自定義的 composables/
或其他地方使用 useCookie
,重新渲染畫面可能會出現 cookie 回到預設值的問題。
此處僅稍微帶過 useCookie 的使用方式,關於如何設定 cookie 的 maxAge
、httpOnly
、domain
... 等,可以參考 useCookie。
🌞 其他還有像 useFetch
、useRoute
、useState
... 等常用且重要的 composables 未來會陸續出現在每一天的文章中
如果要建立自訂的 Compsables 可以參考以下步驟。
建立 composables/
資料夾。
建立 Composables:
方法一:使用具名導出
// composables/useMouseX.ts
export const useMouseX = () => {
const x = useState('x', () => 0)
const Update = (event: any) => {
x.value = event.pageX
}
onMounted(() => window.addEventListener('mousemove', Update))
onUnmounted(() => window.removeEventListener('mousemove', Update))
return { x }
}
方法二:使用預設導出
// composables/useMouseY.ts
export default function () {
const y = useState('y', () => 0)
const Update = (event: any) => {
y.value = event.pageY
}
onMounted(() => window.addEventListener('mousemove', Update))
onUnmounted(() => window.removeEventListener('mousemove', Update))
return { y }
}
使用時 Nuxt 會自動轉換成該檔名的 camelCase 形式(不包含副檔名),例如上述範例若將 useMouseY.ts
改為 use-mouseY.ts
,使用時仍然是 useMouseY()
。
現在可以在 .js
、.ts
和 .vue
檔案中使用自動引入的 Composables。
//app.vue
<template>
<h1>游標座標</h1>
<h2>x: {{ x }}</h2>
<h2>y: {{ y }}</h2>
</template>
// app.vue
<script setup lang="ts">
const { x } = useMouseX()
const { y } = useMouseY()
</script>
Nuxt 只會掃描在 composables/
目錄下的頂層檔案,範例如下:
composables 掃描
┣ index.ts ✅
┣ useCounter.ts ✅
┗ mouse
┣ useMouseX.ts ❌
┗ useMouseY.ts ❌
⭐ Nuxt 3 提供兩個方式使得在巢狀結構內的 composables 也可以被 Nuxt 掃描:
(推薦)在 composables/index.ts
重新導出:
// composables/index.ts
export { useMouseX } from './mouse/useMouseX.ts'
修改 nuxt.config.ts
中的設定:
// nuxt.config.ts
export default defineNuxtConfig({
imports: {
dirs: [
// 掃描頂層模組
'composables',
// 掃描特定名稱和副檔名的第一層模組
'composables/*/index.{ts,js,mjs,mts}',
// 或掃描指定目錄中的所有模組
'composables/**'
]
}
})
今天提到的 Composables 是將有狀態邏輯寫成一個可重用的函式,那麼我們該如何建立共用的無狀態邏輯(Stateless Logic)函式?該存放在哪個資料夾中呢?明日揭曉!